<?php
namespace Tbf\Bundle\SerializerBundle;
use Tbf\Bundle\SerializerBundle\Handler\HandlerRegistry;
use Tbf\Bundle\SerializerBundle\Register\Register;
use Tbf\Component\Classes\ClassFunction;
use Tbf\Component\Debug\Debug;
use Tbf\Component\GiveName\GiveNameFunction;

class Serializer
{
    protected $registry;
    public function __construct(Register $registry)
    {
        $this->registry = $registry;
    }
    /**
     * 将数据序列化成数组
     * 对象的默认行为:
     * 1.使用对象的所有get方法获取数据
     * 2.获取对象的所有public数据
     * 3.如果遇到对象,除非有某个递归结构里面有注册处理方法,否则不处理..
     */
    public function toArray($obj,$group=null)
    {
        if (is_array($obj)) {
            $output = array();
            foreach ($obj as $key=>$value) {
                $output[$key] = $this->toArray($value,$group);
            }
            return $output;
        }
        if (!is_object($obj)) {
            return $obj;
        }
        $handler = $this->registry->getToArray(get_class($obj));
        if ($handler!==null) {
            $ret= $handler->toArray($obj,$group);
            return $ret;
        }
        $output = array();
        foreach($this->toArrayByProperty($obj) as $k1=>$v1){
            $output[$k1] = $this->toArray($v1,$group);
        }
        return $output;
    }

    /**
     * 从数组新建数据
     * 需要类型信息
     */
    public function newFromArray($class_name,$data,$group=null)
    {
        $class_name = ltrim($class_name,'\\');
        $handler = $this->registry->getNewFromArray($class_name);
        if ($handler!==null) {
            return $handler->newFromArray($class_name,$data,$group);
        }
        $obj = $this->newInstanceWithoutConstructor($class_name);
        $this->loadFromArray($obj,$data,$group);

        return $obj;
    }

    /**
     * 从数组加载数据
     * 需要类型信息
     */
    public function loadFromArray($obj,$data,$group=null)
    {
        if (!is_object($obj)) {
            return ;
        }
        $handler = $this->registry->getLoadFromArray(get_class($obj));
        if ($handler!==null) {
            $handler->loadFromArray($obj,$data,$group);
            return;
        }
        $this->loadFromArrayByProperty($obj,$data);

        return ;
    }
    protected $toArrayByGetterCache=array();
    /**
     * getter自动处理,只处理一层,不递归
     * 只处理对象
     * @param $obj
     * @return mixed
     */
    function toArrayByGetter($obj){
        if (!is_object($obj)) {
            return $obj;
        }
        $output = array();
        foreach($this->getToArrayByGetterMeta(get_class($obj)) as $key_name=>$meta_method){
            $output[$key_name] = $meta_method->invoke($obj);
        }
        return $output;
    }
    function toArrayByGetterExtraField($obj,$extra_field=array()){
        if (!is_object($obj)) {
            return $obj;
        }
        $output = array();
        $extra_field = array_fill_keys($extra_field,1);
        foreach($this->getToArrayByGetterMeta(get_class($obj)) as $key_name=>$meta_method){
            if (!empty($extra_field[$key_name])){
                continue;
            }
            $output[$key_name] = $meta_method->invoke($obj);
        }
        return $output;
    }
    protected function getToArrayByGetterMeta($class_name){
        if (!empty($this->toArrayByGetterCache[$class_name])){
            return $this->toArrayByGetterCache[$class_name];
        }
        $meta_class = $this->getReflectionClass($class_name);
        $getter_meta = array();
        foreach($meta_class->getMethods() as $meta_method){
            if (!$meta_method->isPublic()){
                continue;
            }
            $name = $meta_method->getName();
            if (strpos($name,'get')!==0){
                continue;
            }
            if ($meta_method->getNumberOfRequiredParameters()!==0){
                continue;
            }
            $key_name = substr($name,3);
            $key_name = GiveNameFunction::to($key_name,'GiveName','give_name');
            $getter_meta[$key_name] = $meta_method;
        }
        $this->toArrayByGetterCache[$class_name] = $getter_meta;
        return $getter_meta;
    }
    /**
     * property自动处理,只处理一层,不递归
     * 只处理对象
     * @param $obj
     * @return mixed
     */
    function toArrayByProperty($obj){
        if (!is_object($obj)) {
            return $obj;
        }
        $output = array();
        $meta_class = $this->getReflectionClass(get_class($obj));
        //getter自动处理
        foreach ($meta_class->getProperties() as $meta_property) {
            $meta_property->setAccessible(true);
            $name = $meta_property->getName();
            $value = $meta_property->getValue($obj);
            $output[$name] = $value;
        }
        return $output;
    }
    /**
     * property自动处理,只处理一层,不递归
     * 只处理对象
     * @param $obj
     * @return mixed
     */
    function loadFromArrayByProperty($obj,$data){
        if (!is_object($obj)) {
            return $obj;
        }
        $meta_class = $this->getReflectionClass(get_class($obj));
        foreach ($meta_class->getProperties() as $meta_property) {
            $name = $meta_property->getName();
            if (array_key_exists($name,$data)) {
                $meta_property->setAccessible(true);
                $meta_property->setValue($obj,$data[$name]);
            }
        }
        return $obj;
    }

    /**
     * 不通过construct创建对象
     * @param $class_name
     */
    function newInstanceWithoutConstructor($class_name){
        $meta_class = $this->getReflectionClass($class_name);
        return ClassFunction::newInstanceWithoutConstructor($meta_class);
    }

    function getRegister(){
        return $this->registry;
    }

    protected $meta_class_map = array();

    /**
     * @param $class_name
     * @return \ReflectionClass
     */
    function getReflectionClass($class_name){
        if (!empty($this->meta_class_map[$class_name])){
            return $this->meta_class_map[$class_name];
        }
        $this->meta_class_map[$class_name] = new \ReflectionClass($class_name);
        return $this->meta_class_map[$class_name];
    }
}
